/* 
 * Copyright 2012 Mikaël GUILLEMOT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.g2de.gui;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import org.g2de.display.ActionProvider;
import org.g2de.display.MouseControlListener;
import org.g2de.display.Renderable;
import org.g2de.tools.Const;

/**
 * Base de tous les elements d'UI : les UI ont une zone active defini ici. Une
 * methode Activate qui les inscrit ou non au moteur. Et un layer defini en
 * interne, hérité du parent si existant.
 * 
 * Cette classe defini le foctionnement des objets de l'ui, les interfaces sont
 * géré pour aceuillir d'autres composant. Pour simplifier la conception, les
 * ajout de composant sont géré ici. La definition du comportement spécifique se
 * fait dans les methodes : selfBehavior et selfRender
 * 
 * Notez que les les UI n'ont pas de layer (le mecanispe est géré en ammont)
 * Notez que le mecanisme de remontée d'action est a prendre en charge lors de
 * l'imprementation. Notez que la taille par defaut est de 0,0 !
 * 
 * @author Mikael GUILLEMOT
 * @version 1.0
 * @since
 * 
 */
public abstract class AbstractUI extends ActionProvider implements Renderable, MouseControlListener {

	protected Rectangle activeZone;
	private final List<AbstractUI> children;
	private boolean isEnable = true;
	private boolean isVisible = true;
	private boolean propagate = false;
	private boolean oldProp = false;

	/**
	 * constructeur de base
	 */
	public AbstractUI() {
		super();
		activeZone = new Rectangle(0, 0, 0, 0);
		children = new ArrayList<AbstractUI>();
	}

	/**
	 * permet d'ajouter des objets d'ui a l'objet courrant (contenaire)
	 * 
	 * @param ui
	 */
	protected final void add(AbstractUI ui) {
		children.add(ui);
	}

	/**
	 * permet de retirer des objets d'ui a l'objet courrant (contenaire)
	 * 
	 * @param ui
	 */
	protected final void remove(AbstractUI ui) {
		children.remove(ui);
	}

	/**
	 * initialise la taille de la zone active (detection de la souris) par
	 * defaut la zone est vide
	 * 
	 * @param w
	 * @param h
	 */
	public final void setSize(int w, int h) {
		activeZone.width = w;
		activeZone.height = h;
	}

	/**
	 * initialise la taille de la zone active
	 * 
	 * @param x
	 * @param y
	 */
	public final void setOrigin(int x, int y) {
		activeZone.x = x;
		activeZone.y = y;
	}

	/**
	 * renvoi la zone active (non modifiable)
	 * 
	 * @return
	 */
	public final Rectangle getActiveZone() {
		final Rectangle r = new Rectangle(activeZone);
		return r;
	}

	/**
	 * controle l'etat d'ecoute du composant
	 * 
	 * @param enable
	 */
	public final void setEnable(boolean e) {
		isEnable = e;
	}

	/**
	 * controle la visibilitée ET l'etat d'ecoute du composant
	 * 
	 * @param enable
	 */
	public final void setVisible(boolean e) {
		isVisible = e;
		isEnable = e;
	}

	@Override
	public final void display(Graphics2D buffer) {
		if (isVisible) {
			selfRender(buffer);
			for (Renderable r : children)
				r.display(buffer);
		}
		if (Const.DEBUG) {
			if (isVisible)
				buffer.setColor(Color.MAGENTA);
			else
				buffer.setColor(Color.ORANGE);
			buffer.drawRect(activeZone.x, activeZone.y, activeZone.width, activeZone.height);
		}
	}

	/**
	 * renvoi une valeur de comparaison pour les objets affichables
	 */
	public int getComparaisonValue() {
		return this.activeZone.y;
	}

	/**
	 * comparaison selon les Y : normalement inutile pour l'ui si elle est bien
	 * concue. cette technique permet de gérer la profondeur en rendu
	 * isometrique
	 */
	@Override
	public final int compareTo(Renderable o) {
		return this.activeZone.y - o.getComparaisonValue();
	}

	@Override
	public final void mouseChange(MouseEvent me, short change) {
		propagate = activeZone.contains(me.getPoint()) && isEnable;

		// traitement drag sans decrochage
		if (change == MouseControlListener.EV_DRAG)
			this.selfBehavior(me, change);
		else
		// traitement entrée/sortie de zone
		if (oldProp && !propagate) {
			oldProp = propagate;
			this.selfBehavior(me, MouseControlListener.EV_EXITED);
			for (MouseControlListener cl : children)
				cl.mouseChange(me, change);
		} else if (!oldProp && propagate) {
			oldProp = propagate;
			this.selfBehavior(me, MouseControlListener.EV_ENTER);
			for (MouseControlListener cl : children)
				cl.mouseChange(me, change);
		} else
		// traitement normal
		if (propagate) {
			this.selfBehavior(me, change);
			for (MouseControlListener cl : children)
				cl.mouseChange(me, change);
		}

	}

	/**
	 * permet de gérer les evenement localement. si aucun comportement n'est
	 * attendu, la methode peut etre vide. Cette methode n'est appelé que si
	 * l'evenement se produit dans la zone active (Rectangle activeZone)
	 * 
	 * @param arg0
	 * @param isClick
	 */
	protected abstract void selfBehavior(MouseEvent arg0, short action);

	/**
	 * permet de gérer l'affichage localement. si aucun comportement n'est
	 * attendu, la methode peut etre vide
	 * 
	 * @param buffer
	 */
	protected abstract void selfRender(Graphics2D buffer);

	/**
	 * Supprime les references sur l'objet et ses enfants
	 * 
	 * @return null
	 */
	public final AbstractUI dispose() {
		this.setVisible(false);
		for (AbstractUI ui : children)
			ui.dispose();
		children.clear();
		this.activeZone = null;
		return null;
	}

}
